﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using SudokuHelper.Appearance;
using SudokuHelper.Model;
using SudokuHelper.Model.Values;

namespace SudokuHelper.Views {
    public class CellView : Label, ICellView, IValueVisitor {
        #region Private members

        private CellAppearance aDefault = new CellAppearance(),
                               aSelected = new CellAppearance(),
                               aError = new CellAppearance(),
                               aCurrent = null;

        private int row, col;
        private ISudokuDesk desk;
        private bool isSelected;
        private bool isErroneous;

        private static readonly Font CellDefaultFont = new Font("Arial", 20, FontStyle.Bold);
        private static readonly Font CellRangeFont = new Font("Courier New", 10);

        #endregion

        #region Constructors

        public CellView(int row, int col) {
            this.row = row;
            this.col = col;

            aCurrent = aDefault;

            AutoSize = false;
            TextAlign = ContentAlignment.MiddleCenter;
            Margin = new Padding(0);
            Padding = new Padding(0);
            Font = CellDefaultFont;
        }

        #endregion

        #region Implementation of ICellView

        public string ErrorValue { get; set; }

        public ISudokuDesk Desk {
            set {
                if(desk != null) {
                    desk.CellValueChanged -= desk_CellValueChanged;
                }

                desk = value;

                if(desk != null) {
                    desk.CellValueChanged += desk_CellValueChanged;
                }
            }
        }

        public bool IsSelected {
            get { return isSelected;}
            set {
                if(isSelected = value) {
                    aCurrent = aSelected;
                } else if(isErroneous) {
                    aCurrent = aError;
                } else{
                    aCurrent = aDefault;
                }
                
                ApplyAppearance();

            }
        }

        public bool IsErroneous {
            get { return isErroneous; } 
            set {
                if(isErroneous = value){
                    Text = ErrorValue;
                    aCurrent = aError;
                } else {
                    if(IsSelected) {
                        aCurrent = aSelected;
                    } else {
                        aCurrent = aDefault;
                    }
                    Text = "";
                }
                ApplyAppearance();
            }
        }

        public CellAppearance AppearanceDefault {
            get { return aDefault; }
        }

        public CellAppearance AppearanceSelected {
            get { return aSelected; }
        }

        public CellAppearance AppearanceError {
            get { return aError; }
        }

        #endregion

        #region Implementation of IValueVisitor

        object IValueVisitor.Visit(UserValue value, object arg) {
            Font = CellDefaultFont;
            ApplyAppearance();
            Text = value.ToString();
            return null;
        }

        object IValueVisitor.Visit(CalculatedValue value, object arg) {
            Font = CellDefaultFont;
            ApplyAppearance();
            Text = "[" + value + "]";
            return null;
        }

        object IValueVisitor.Visit(ValueRange valueRange, object arg) {
            StringBuilder b = new StringBuilder();
            int i = 0;
            foreach (var value in valueRange.AvailableValues) {
                b.Append(value);
                if (++i >= 3) {
                    i = 0;
                    b.Append((char) 13).Append((char) 10);
                } else {
                    b.Append(" ");
                }
            }
            b.Length -= 1;
            Font = CellRangeFont;
            Text = b.ToString();
            return null;
        }

        #endregion

        #region Utility methods

        private void ApplyAppearance() {
            this.BackColor = aCurrent.BackColor;
            this.ForeColor = aCurrent.ForeColor;
            Invalidate();
        }

        #endregion

        #region Event handling routines

        private void desk_CellValueChanged(object sender, CellValueChangedEventArgs e) {
            if(row==e.Row && col==e.Column){
                e.Value.Accept(this, null);
            }
        }

        #endregion

        #region Overriden members

        protected override void OnPaint(PaintEventArgs e) {
            base.OnPaint(e);

            using (Pen p = new Pen(aCurrent.BorderColor)){
                e.Graphics.DrawRectangle(p, 0, 0, ClientSize.Width - 1, ClientSize.Height - 1);
            }
        }

        #endregion
    }
}
